package com.example.filestorage.starter.service;

import com.example.filestorage.starter.FileStorageProperties;
import com.example.filestorage.starter.exception.FileStorageException;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
@ConditionalOnProperty(name = "file.storage.type", havingValue = "rustfs")
public class RustFsStorageService implements StorageService {

    private MinioClient minioClient;
    private final FileStorageProperties fileStorageProperties;

    public RustFsStorageService(FileStorageProperties fileStorageProperties) {
        this.fileStorageProperties = fileStorageProperties;
    }

    @PostConstruct
    private void initialize() {
        if (fileStorageProperties.getRustfs().isEnabled()) {
            try {
                // 创建MinIO客户端连接到RustFS (因为RustFS兼容S3 API)
                this.minioClient = MinioClient.builder()
                        .endpoint(fileStorageProperties.getRustfs().getEndpoint())
                        .credentials(fileStorageProperties.getRustfs().getAccessKey(), fileStorageProperties.getRustfs().getSecretKey())
                        .build();
                
                createBucketIfNotExists();
            } catch (Exception e) {
                throw new FileStorageException("Failed to initialize RustFS storage: " + e.getMessage(), e);
            }
        }
    }
    
    private void createBucketIfNotExists() {
        try {
            String bucketName = fileStorageProperties.getRustfs().getBucketName();
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            // 如果存储桶创建失败，我们记录警告但不抛出异常
            // 应用程序仍然可以尝试使用存储桶，如果它已经存在
            System.err.println("Warning: Could not create bucket '" + fileStorageProperties.getRustfs().getBucketName() + "': " + e.getMessage());
        }
    }

    @Override
    public String storeFile(MultipartFile file) {
        if (!fileStorageProperties.getRustfs().isEnabled()) {
            throw new FileStorageException("RustFS storage is not enabled");
        }
        
        String bucketName = fileStorageProperties.getRustfs().getBucketName();
        
        // Normalize file name
        String originalFileName = StringUtils.cleanPath(file.getOriginalFilename());

        try {
            // Check if the file's name contains invalid characters
            if (originalFileName.contains("..")) {
                throw new FileStorageException("Sorry! Filename contains invalid path sequence " + originalFileName);
            }

            // Generate unique file name to avoid conflicts
            String uniqueFileName = UUID.randomUUID().toString() + "_" + originalFileName;

            // Upload file to RustFS using MinIO client
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(
                PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(uniqueFileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build()
            );

            return uniqueFileName;
        } catch (Exception ex) {
            throw new FileStorageException("Could not store file " + originalFileName + ". Please try again!", ex);
        }
    }

    @Override
    public Resource loadFileAsResource(String fileName) {
        if (!fileStorageProperties.getRustfs().isEnabled()) {
            throw new FileStorageException("RustFS storage is not enabled");
        }
        
        String bucketName = fileStorageProperties.getRustfs().getBucketName();
        
        try {
            InputStream inputStream = minioClient.getObject(
                GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build()
            );
            
            return new InputStreamResource(inputStream);
        } catch (Exception ex) {
            throw new FileStorageException("File not found " + fileName, ex);
        }
    }
    
    @Override
    public void deleteFile(String fileName) {
        if (!fileStorageProperties.getRustfs().isEnabled()) {
            throw new FileStorageException("RustFS storage is not enabled");
        }
        
        String bucketName = fileStorageProperties.getRustfs().getBucketName();
        
        try {
            minioClient.removeObject(
                RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build()
            );
        } catch (Exception ex) {
            throw new FileStorageException("Could not delete file " + fileName, ex);
        }
    }
    
    @Override
    public boolean isEnabled() {
        return fileStorageProperties.getRustfs().isEnabled();
    }
    
    @Override
    public List<String> listFiles() {
        if (!fileStorageProperties.getRustfs().isEnabled()) {
            return new ArrayList<>();
        }
        
        String bucketName = fileStorageProperties.getRustfs().getBucketName();
        List<String> fileNames = new ArrayList<>();
        
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .build()
            );
            
            for (Result<Item> result : results) {
                Item item = result.get();
                fileNames.add(item.objectName());
            }
            
            return fileNames;
        } catch (Exception ex) {
            throw new FileStorageException("Could not list files", ex);
        }
    }
}